﻿using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using WpfApp1.Models;
using WpfApp1.ViewModels;

namespace WpfApp1.Controls
{
    public class DiagramCanvas : Canvas
    {
        /// <summary>
        /// 连接线
        /// </summary>
        private LinkBase _partialLink;

        /// <summary>
        /// 起始连接点控件
        /// </summary>
        private PortControl _sourcePort;
        public PortControl SourcePort
        {
            get => _sourcePort;
            set
            {
                if (_sourcePort != value)
                {
                    _sourcePort = value;
                    if (_sourcePort != null)
                    {
                        PortBase sourceDataItem = _sourcePort.Info;
                        _partialLink = new LinkBase(sourceDataItem, new PartPort(sourceDataItem.Position.X + 4, sourceDataItem.Position.Y + 4));

                        MainWindowViewModel? mainViewModel = DataContext as MainWindowViewModel;
                        if (mainViewModel == null) return;
                        mainViewModel.Items.Add(_partialLink);
                    }

                }
            }
        }

        /// <summary>
        /// 终止连接点控件
        /// </summary>
        private PortControl _targetPort;
        public PortControl TargetPort { get => _targetPort; set { if (_targetPort != value) { _targetPort = value; } } }


        private Point? startPoint = null;
        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);

            if (e.LeftButton == MouseButtonState.Pressed && e.Source == this)
            {
                startPoint = e.GetPosition(this);
            }

            //e.Handled = true;
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            // if mouse button is not pressed we have no drag operation, ...
            if (e.LeftButton != MouseButtonState.Pressed)
            {
                startPoint = null;
            }

            //Debug.WriteLine($"DiagramCanvas---AddAdorner---OnMouseMove1--{DateTime.Now.ToString("yyyyMMdd-HHmmssfff")}");
            // ... but if mouse button is pressed and start point value is set we do have one
            if (this.startPoint.HasValue)
            {
                // create rubberband adorner
                AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                if (adornerLayer != null)
                {
                    DrawAdorner adorner = new DrawAdorner(this, startPoint);
                    if (adorner != null)
                    {
                        adornerLayer.Add(adorner);
                        Debug.WriteLine("DiagramCanvas---AddAdorner");
                    }
                }

                Debug.WriteLine("DiagramCanvas---AddAdorner---OnMouseMove2");
            }

            if (SourcePort != null && e.LeftButton == MouseButtonState.Pressed)
            {
                Point currentPoint = e.GetPosition(this);
                _partialLink.TargetPort.Position = currentPoint;
                //设置DiagramCanvas层中的终止连接点属性
                TargetPort = HitTesting<PortControl>(currentPoint);
            }

            //e.Handled = true;
        }
        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);

            MainWindowViewModel? mainViewModel = DataContext as MainWindowViewModel;
            if (mainViewModel == null) return;
            mainViewModel.Items.Remove(_partialLink);
            if (SourcePort != null && TargetPort != null)
            {
                PortBase sourcePort = SourcePort.Info;
                PortBase targetPort = TargetPort.Info;
                mainViewModel.Items.Add(new LinkBase(sourcePort, targetPort));
            }
            _partialLink = null;
            SourcePort = null;
            TargetPort = null;

            //e.Handled = true;
        }

        protected override void OnDragEnter(DragEventArgs e)
        {
            base.OnDragEnter(e);
        }
        protected override void OnDragOver(DragEventArgs e)
        {
            DragObject? dragObject = e.Data.GetData(typeof(DragObject)) as DragObject;
            base.OnDragOver(e);
        }
        protected override void OnDragLeave(DragEventArgs e)
        {
            base.OnDragLeave(e);
        }
        protected override void OnDrop(DragEventArgs e)
        {
            /* 在WPF（Windows Presentation Foundation）中，OnDrop 方法是作为UI元素（尤其是自定义控件或窗口）的一部分来处理拖放操作的。需要注意以下几点：

                未启用允许拖放：确保你的窗口或控件已经设置了 AllowDrop 属性为 true。这是接受拖放操作的基本要求。
                DragOver 事件未处理：需要在 DragOver 事件中调用 e.Effects = DragDropEffects.Copy 或其他允许的效果，并且调用 e.Handled = true 来阻止事件的默认处理，这允许 drop 操作发生。
                事件处理逻辑错误：检查 OnDrop 方法内部的逻辑，确保没有错误地阻止了方法的正常执行，比如异常未被捕获等。
                数据格式不匹配：在 OnDrop 事件中，通过 DragEventArgs.Data 获取的数据需要与你的应用期望的数据格式匹配。如果不匹配，可能会导致逻辑不被执行。
                UI 更新问题：WPF 中UI更新可能需要在特定的线程上下文中进行，确保在 OnDrop 处理过程中正确使用了 Dispatcher 如果涉及到UI更新。
                权限问题：在一些安全限制较高的环境中，拖放功能可能会被限制。
                外部干扰：如果有其他覆盖在目标上的控件或者弹出窗口等，它们可能会拦截掉拖放事件。
                依赖属性或绑定冲突：有时候，绑定或依赖属性的设置可能意外地影响了拖放行为。
            */

            DragObject? dragObject = e.Data.GetData(typeof(DragObject)) as DragObject;
            if (dragObject != null)
            {
                Point position = e.GetPosition(this);

                var lineBase = Activator.CreateInstance(dragObject.SharpBase.GetType()) as SharpBase;
                if (lineBase == null) return;
                lineBase.Left = position.X;
                lineBase.Top = position.Y;

                MainWindowViewModel? mainViewModel = DataContext as MainWindowViewModel;
                if (mainViewModel == null) return;
                mainViewModel.Items.Add(lineBase);
                Debug.WriteLine("OnDrop");
            }

            e.Handled = true;
            Focus();
            base.OnDrop(e);
        }



        protected override void OnRender(DrawingContext dc)
        {
            var rect = new Rect(0, 0, RenderSize.Width, RenderSize.Height);
            dc.DrawRectangle(Background, null, rect);
            DrawGrid(dc, rect);
        }
        protected virtual void DrawGrid(DrawingContext dc, Rect rect)
        {
            //using .5 forces wpf to draw a single pixel line
            int offset = 0;
            for (var i = offset + 0.5; i < rect.Height - offset; i += 100)
            {
                dc.DrawLine(new Pen(new SolidColorBrush(Colors.Gray), 1), new Point(offset, i), new Point(rect.Width - offset, i));
            }
            dc.DrawLine(new Pen(new SolidColorBrush(Colors.Gray), 1), new Point(offset, rect.Height - offset), new Point(rect.Width - offset, rect.Height - offset));

            for (var i = offset + 0.5; i < rect.Width - offset; i += 100)
            {
                dc.DrawLine(new Pen(new SolidColorBrush(Colors.Gray), 1), new Point(i, offset), new Point(i, rect.Height - offset));
            }
            dc.DrawLine(new Pen(new SolidColorBrush(Colors.Gray), 1), new Point(rect.Width - offset, offset), new Point(rect.Width - offset, rect.Height - offset));
        }


        private T HitTesting<T>(Point hitPoint) where T : class
        {
            DependencyObject hitObject = this.InputHitTest(hitPoint) as DependencyObject;
            while (hitObject != null && hitObject.GetType() != typeof(DiagramCanvas))
            {
                if (hitObject is T port)
                {
                    return port;
                }
                hitObject = VisualTreeHelper.GetParent(hitObject);
            }

            return null;
        }

    }
}
